﻿

using System;
using System.Collections;
using System.Xml.Linq;

namespace 贪吃蛇
{
    internal class Play : Game
    {
        internal static Snake snake = new Snake("●", "◎", new Pos(bufWidth / 2, bufHeight / 2));// head body headposition
        Food food = new();
        E_Direct direct = E_Direct.Right;
        public static E_Speed speed = E_Speed.Quick;
        bool gameOver = false;
        bool press = false;
        public override void play()
        {
            reset();
            Console.Clear();
            //Console.WriteLine("playing");
            writeBlock();
            writeSnake(snake);
            Thread thread = new Thread(waitInput);
            thread.Start();
            food.add();
            while (!gameOver)
            {
                erase(snake); //可以eaten时不擦除尾部
                move(snake, direct);
                if (isCrush(snake))
                {
                    gameOver = true;
                    continue;
                }
                writeSnake(snake);
                if (isEaten(food, snake))
                {
                    food.add();
                    food.eaten = true;
                }
                Thread.Sleep((int)speed);
            }
            writeOverInfo();
            Game.scene = E_Scene.Over;
            if (thread.IsAlive)
            {
                while(!press)
                {
                }
            }
            else
            {
                Console.ReadKey(true);
            }
        }

        private void reset()
        {
            snake = new Snake("●", "◎", new Pos(bufWidth / 2, bufHeight / 2));// head body headposition
            food = new();
            direct = E_Direct.Right;
            gameOver = false;
        }

        private bool isEaten(Food food, Snake snake)
        {
            if (food.pos.x - 1 <= snake.headPos.x && food.pos.x + 1 > snake.headPos.x
                    && food.pos.y == snake.headPos.y)
                //if (food.pos.x == snake.headPos.x && food.pos.y == snake.headPos.y)
                return true;
            return false;
        }

        private void writeOverInfo()
        {
            Console.SetCursorPosition(bufWidth / 2 - 2, bufHeight / 2);
            Console.ForegroundColor = ConsoleColor.Red;
            Console.Write("游戏结束!");
        }

        private bool isCrush(Snake snake)
        {
#pragma warning disable CS8602
            Pos pos = snake.poses.First.Value;
            if (pos.x < 2 || pos.x >= bufWidth - 3 ||
                pos.y < 1 || pos.y >= bufHeight - 1)
            {
                return true;
            }
#pragma warning disable CS8600
            LinkedListNode<Pos> curPos = Play.snake.poses.First;
            while (curPos.Next != null)
            {
                curPos = curPos.Next;
                if (curPos.Value.x == pos.x && curPos.Value.y == pos.y)
                   return true;
            }
            return false;

        }

        public void waitInput()
        {
            while (!gameOver)
            {
                switch (Console.ReadKey(true).Key)
                {
                    case ConsoleKey.W:
                        if(direct == E_Direct.Down)
                            break;
                        direct = E_Direct.Up;
                        break;
                    case ConsoleKey.S:
                        if (direct == E_Direct.Up)
                            break;
                        direct = E_Direct.Down;
                        break;
                    case ConsoleKey.A:
                        if (direct == E_Direct.Right)
                            break;
                        direct = E_Direct.Left;
                        break;
                    case ConsoleKey.D:
                        if (direct == E_Direct.Left)
                        break;
                        direct = E_Direct.Right;
                        break;
                    default:
                        press = true;
                        break;
                }
            }

        }

        private void erase(Snake snake)
        {
            if (snake.poses.First == null)
            {
                return;
            }
            LinkedListNode<Pos> curPos = snake.poses.First;
            while (curPos != null)
            {
                Console.SetCursorPosition(curPos.Value.x, curPos.Value.y);
                Console.ForegroundColor = ConsoleColor.Green;
                Console.Write("  ");
                curPos = curPos.Next;
            }
        }

        private void writeBlock()
        {
            String block = "■";
            Console.ForegroundColor = ConsoleColor.Red;
            for (int i = 0; i < bufWidth - 1; i++)
            {
                Console.SetCursorPosition(i, 0);
                Console.Write(block);
                Console.SetCursorPosition(i, bufHeight - 1);
                Console.Write(block);
            }
            for (int j = 1; j < bufHeight - 1; j++)
            {
                Console.SetCursorPosition(0, j);
                Console.Write(block);
                Console.SetCursorPosition(bufWidth - 2, j);
                Console.Write(block);
            }
        }

        private void move(Snake snake, E_Direct direct)
        {
            switch (direct)
            {
                case E_Direct.Up:
                    snake.headPos.y--;
                    break;
                case E_Direct.Down:
                    snake.headPos.y++;
                    break;
                case E_Direct.Left:
                    snake.headPos.x -= 2;
                    break;
                case E_Direct.Right:
                    snake.headPos.x += 2;
                    break;
                default:
                    break;
            }
            snake.poses.AddFirst(snake.headPos);
            if(!food.eaten)
                snake.poses.RemoveLast();
            food.eaten = false;
        }

        private void writeSnake(Snake snake)
        {
            if (snake.poses.First == null)
            {
                Console.WriteLine("xxxx");
                return;
            }
            LinkedListNode<Pos> curPos = snake.poses.First;
            Console.SetCursorPosition(curPos.Value.x, curPos.Value.y);
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.Write(snake.head);
            while (curPos.Next != null)
            {
                curPos = curPos.Next;
                Console.SetCursorPosition(curPos.Value.x, curPos.Value.y);
                Console.ForegroundColor = ConsoleColor.Green;
                Console.Write(snake.body);
            }
            snake.tailPos = curPos.Value;
        }
    }
    class Snake
    {
        internal String head;
        internal String body;
        internal LinkedList<Pos> poses = new LinkedList<Pos>();
        internal Pos headPos; //移动后实时变化
        internal Pos tailPos; //尾部用于吃了后画
        //internal Pos tailPos; //似乎不需要
        public Snake()
        { }
        public Snake(String head, String body, Pos headPos)
        {
            this.head = head;
            this.body = body;
            this.headPos = headPos;
            //this.tailPos = headPos;
            poses.AddFirst(headPos);
        }
        
}

    internal class Food
    {
        internal String body;
        internal ConsoleColor color;
        internal Pos pos;
        internal bool eaten;
        bool spawn = true;
        public Food()
        {
            body = "☼";
            color = ConsoleColor.Blue;
        }
        internal void add()
        {
            while (true)
            {
                Random r = new Random();
                pos.x = r.Next(3, Play.bufWidth - 4);
                pos.y = r.Next(1, Play.bufHeight - 2);
                #pragma warning disable CS8600 
                LinkedListNode<Pos> curPos = Play.snake.poses.First;
                while (curPos != null)
                {
                    if (curPos.Value.x == pos.x && curPos.Value.y == pos.y)
                        spawn = false;
                        
                    curPos = curPos.Next;
                }
                if (spawn)
                    break;
            }
            Console.SetCursorPosition(pos.x, pos.y);
            Console.ForegroundColor = color;
            Console.Write(body);
        }
    }

    struct Pos
    {
        internal int x, y;
        public Pos(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
    }
    enum E_Direct
    {
        Up, Down, Left, Right
    }
    enum E_Speed
    {
        VeryQuick = 50,
        Quick = 100,
        Normal = 150,
        Slow = 200
    }
}